#include <OpenMesh/Core/IO/MeshIO.hh>
#include "qopenmesh.h"
#include <OpenMesh/Core/Mesh/PolyConnectivity.hh>

QOpenMesh::QOpenMesh()
    :QMesh()
{
}

QOpenMesh::~QOpenMesh()
{
}

QOpenMesh::QOpenMesh(const QOpenMesh &rhs)
    :QMesh(),
    m_mesh(rhs.m_mesh),
    m_indices(rhs.m_indices)
{

}

int QOpenMesh::nVertices()
{
    return m_mesh.n_vertices();
}

const float *QOpenMesh::Vertices()
{
	return m_mesh.points()->data();
}

const float *QOpenMesh::Normals()
{
    return m_mesh.vertex_normals()->data();
}

uint* QOpenMesh::Indices()
{
    return m_indices.data();
}

uint QOpenMesh::UploadArrayData(int index)
{
    uint vboid;
    glGenBuffers(1,&vboid);
    glBindBuffer(GL_ARRAY_BUFFER,vboid);
    switch(index)
    {
    case QVERTEX:
        glBufferData(GL_ARRAY_BUFFER,sizeof(float)*m_mesh.n_vertices()*3,Vertices(),GL_STATIC_DRAW);
		break;
	case QNORMAL:
        glBufferData(GL_ARRAY_BUFFER,sizeof(float)*m_mesh.n_vertices()*3,Normals(),GL_STATIC_DRAW);
		break;
    case QFACEVERTEX:
		{
			QVector<float> _data;
			FaceIter fi = m_mesh.faces_begin();
			for (;fi!=m_mesh.faces_end();++fi)
			{
				fvI fvi = m_mesh.fv_begin(fi.handle());
				for (;fvi;++fvi)
				{
					float* _cdata = m_mesh.point(fvi.handle()).data();
                    _data<<_cdata[0]<<_cdata[1]<<_cdata[2];
				}
			}
			glBufferData(GL_ARRAY_BUFFER,sizeof(float)*_data.size(),_data.data(),GL_STATIC_DRAW);
			break;
		}
    case QFACENORMAL:
        {
            QVector<float> _data;
            FaceIter fi = m_mesh.faces_begin();
            for (;fi!=m_mesh.faces_end();++fi)
            {
                fvI fvi = m_mesh.fv_begin(fi.handle());
                for (;fvi;++fvi)
                {
                    const float* _ndata = m_mesh.normal(fi.handle()).data();
                    _data<<_ndata[0]<<_ndata[1]<<_ndata[2];
                }
            }
            glBufferData(GL_ARRAY_BUFFER,sizeof(float)*_data.size(),_data.data(),GL_STATIC_DRAW);
            break;
        }
    }
	m_vboMap[index] = vboid;
    return vboid;
}

uint QOpenMesh::UploadElementData(int index)
{
	uint vboid;
	glGenBuffers(1,&vboid);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,vboid);
	switch(index)
	{
	case QINDICES:
		glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(uint)*m_indices.size(),m_indices.data(),GL_STATIC_DRAW);
		break;
	case QFACEINDICES:
		{
			QVector<uint> _data(m_indices.size());
			for (int i = 0; i<m_indices.size(); i++)
				_data<<i;
			glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(unsigned int)*_data.size(),_data.data(),GL_STATIC_DRAW);
			break;
		}
	}
	m_vboMap[index] = vboid;
    return vboid;
}

void QOpenMesh::BindArrayBuffer(int index, int programLocation)
{
	glBindBuffer(GL_ARRAY_BUFFER,m_vboMap[index]);
	glVertexAttribPointer(programLocation,3,GL_FLOAT,false,0,0);
}

void QOpenMesh::DrawElements(int indicesIndex, GLenum mode)
{
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,m_vboMap[indicesIndex]);	
	switch(indicesIndex)
	{
	case QFACEINDICES:
	case QINDICES:
		glDrawElements(mode, m_indices.size(), GL_UNSIGNED_INT, 0);
		break;
    }
}

void QOpenMesh::NormNoise(float mean, float std)
{
    for (int i=0;i<nVertices();i++)
    {
        VH cvh(i);
        float* cdata = m_mesh.point(cvh).data();
        const float* cndata = m_mesh.normal(cvh).data();
        QVector3D v3dndata(cndata[0],cndata[1],cndata[2]);
        v3dndata.normalize();
        v3dndata*=normrnd(mean, std);
        m_mesh.set_point(cvh, OMesh::Point(cdata[0]+v3dndata.x(),cdata[1]+v3dndata.y(),cdata[2]+v3dndata.z()));
    }
//    m_mesh.update_face_normals();
//    m_mesh.update_normals();
}

void QOpenMesh::LoadFromFile(char *filepath)
{
    OpenMesh::IO::Options _opt();
    bool flag = OpenMesh::IO::read_mesh(m_mesh, filepath);
    if (!flag)
        qDebug("Fail to read Mesh");
    else
    {
        for (FaceIter fi = m_mesh.faces_sbegin();fi!=m_mesh.faces_end();++fi)
		{
            for (fvI fvi = m_mesh.cfv_iter(fi);fvi;++fvi)
            {
                m_indices<<fvi.handle().idx();
            }
        }
        m_mesh.request_face_normals();
        m_mesh.request_vertex_normals();

		m_mesh.update_face_normals();
		m_mesh.update_vertex_normals();
    }
}
